Kriticke sekce a spol.

Otázka od: Petr Selinger

5. 12. 2002 10:53

Zdravim,
  netusi nekdo jak se pouzivaji spravne kriticke sekce, semafory, atd?

  Resim problem pri vymene dat - odesilatel vysle zpravu,
  prijemce ji zachyti a posle zpatky, odesilatel ji znovu zachyti,
  zkontroluje a posle novou zpravu, atd.

  Problem je, ze po urcite dobe dojde ke StackOverFlow, protoze
  kdyz se procedury pro odeslani a prijem se nestihnou dokoncit, muze
  dojit na zaklade vyvolani eventu k dalsim vstupum do techto
  procedur.

  Napadlo me pouzit neco jako semafory, kdy by to vlakno, ktere
  chce vstoupit do jeste nedokoncene procedury muselo pockat, nez
  se procedura radne ukonci.

  Neresil nekdo nekdy neco podobneho?

  Diky, Petr Selinger

--------------------
Siemens C45 za 2.977 Kč. Nejvýhodnější nabídka telefonů na trhu!
http://user.centrum.cz/redir.php?url=http://www.oskarmobil.cz/handsets/handset.php

Odpovedá: Robert Suska

5. 12. 2002 11:17

Ahoj!

Ja pouzivam semafor ale taky primitivny a asi takto.

mam nejaku globalnu premennu bSemaphore: Boolean; pri initialization si ju
nastavim na True potom mam metodu

procedure Semaphore;
begin
 while bSemaphore do
 begin
  Sleep(1);
  Application.ProcessMessage;
 end;
 bSemaphore:= True; // znovu nastavim ze budem cakat na data
end;

procedure SendData;
begin
 CLS.Socket.SendText('CMD GET COPY FILE'); //   som si len vymyslel
 Semaphore;
end;

procedure ReceiveData;
begin
 sReceiveData:= CLS.Socket.ReceiveText;
 bSemaphore:= False; // mam data a vyskocim s cyklu
end;

initialization

bSemaphore:= True;

------------

tod vsjo.

S pozdravom,

************************************
Robert Suska
KORAK SLOVAKIA s.r.o.
Horna 52
974 01 Banska Bystrica
www.korak.sk robert@korak.sk
Network administrator, Delphi programmer
************************************

> Zdravim,
> netusi nekdo jak se pouzivaji spravne kriticke sekce, semafory, atd?
>
> Resim problem pri vymene dat - odesilatel vysle zpravu,
> prijemce ji zachyti a posle zpatky, odesilatel ji znovu zachyti,
> zkontroluje a posle novou zpravu, atd.
>
> Problem je, ze po urcite dobe dojde ke StackOverFlow, protoze
> kdyz se procedury pro odeslani a prijem se nestihnou dokoncit, muze
> dojit na zaklade vyvolani eventu k dalsim vstupum do techto
> procedur.
>
> Napadlo me pouzit neco jako semafory, kdy by to vlakno, ktere
> chce vstoupit do jeste nedokoncene procedury muselo pockat, nez
> se procedura radne ukonci.
>
> Neresil nekdo nekdy neco podobneho?
>
> Diky, Petr Selinger
>
> --------------------
> Siemens C45 za 2.977 Kč. Nejvýhodnější nabídka telefonů na trhu!
http://user.centrum.cz/redir.php?url=http://www.oskarmobil.cz/handsets/hands
et.php
>
>

Odpovedá: Blazek Jaroslav

5. 12. 2002 11:57

Ahoj,

> delphiforum@centrum.cz 5.12.02 10:39 >>>
> Napadlo me pouzit neco jako semafory, kdy by to vlakno, ktere
> chce vstoupit do jeste nedokoncene procedury muselo pockat, nez
> se procedura radne ukonci.

http://sweb.cz/data.product/Delphi/5/Threads/Threads.zip

mas tam 2 thready, ktere mezi sebou komunikuji pomoci binarnich semaforu


S pozdravem

Bc. Jaroslav Blazek
Access-IT Ceska Lipa
mailto:jaroslav.blazek@access-it.cz
http://www.access-it.cz
ICQ# : 133673990
+420605/813644

Odpovedá: Radek KALA

5. 12. 2002 12:01

Pokud mluvis o windowssovkych zpravach, neposilas je nahodou
prikazem sendmessage ?

Mozna by stacilo dat prikaz postmessage.

> Zdravim,
> netusi nekdo jak se pouzivaji spravne kriticke sekce, semafory, atd?
>
> Resim problem pri vymene dat - odesilatel vysle zpravu,
> prijemce ji zachyti a posle zpatky, odesilatel ji znovu zachyti,
> zkontroluje a posle novou zpravu, atd.
>
> Problem je, ze po urcite dobe dojde ke StackOverFlow, protoze
> kdyz se procedury pro odeslani a prijem se nestihnou dokoncit, muze
> dojit na zaklade vyvolani eventu k dalsim vstupum do techto
> procedur.
>
> Napadlo me pouzit neco jako semafory, kdy by to vlakno, ktere
> chce vstoupit do jeste nedokoncene procedury muselo pockat, nez se
> procedura radne ukonci.
>
> Neresil nekdo nekdy neco podobneho?
>
> Diky, Petr Selinger
>
> --------------------
> Siemens C45 za 2.977 Kč. Nejvýhodnější nabídka telefonů na trhu!
> http://user.centrum.cz/redir.php?url=http://www.oskarmobil.cz/handsets
> /handset.php
>


                     S pozdravem Radek KALA
                     BetaControl, s.r.o.
                     Cerneho 58/60, 635 00
                     tlf. : + 420 5 4622 3491
                     fax : + 420 5 4622 3470
                     GSM : + 420 603 85 75 15

Odpovedá: Petr Vones

5. 12. 2002 13:47

From: "Robert Suska" <delphi@korak.sk>
> mam nejaku globalnu premennu bSemaphore: Boolean; pri initialization si ju
> nastavim na True potom mam metodu
>
> procedure Semaphore;
> begin
> while bSemaphore do
> begin
> Sleep(1);
> Application.ProcessMessage;
> end;
> bSemaphore:= True; // znovu nastavim ze budem cakat na data
> end;

Toto je primo ukazkovy priklad jak se vec rozhodne nema resit. Procedura
Semaphore se ti totiz zevnitr 'Application.ProcessMessage' muze znovu zavolat,
a to i nekolikrat, protoze porusujes zasadu kde v aplikaci ma byt pouze jedno
misto ve kterem se vyrizuji zpravy. Vetsina kodu nebyva reentrantni  

K podobnym vecem prave slouzi eventy a dalsi synchronizacni objekty operacniho
systemu. Nemluve o tom, ze cekani ve smycce na nejaky flag nesmyslne zatezuje
procesor, od toho jsou prave WaitXXX funkce ktere zajisti prepnuti do kernel
modu a tim naznaci scheduleru ze nema danemu thredu pridelovat zadny cas.

Petr Vones

Odpovedá: Petr Selinger

5. 12. 2002 13:18

  Pro ilustraci davam kousek kodu a vysvetlim, ceho bych chtel
  dosahnout.

  ...
  var
    Form1: TForm1;
    Semaphore: THandle;
  ...
  procedure TForm1.Button1Click(Sender: TObject);
  var i: integer;
  begin
    WaitForSingleObject(Semaphore, INFINITE);
    for i := 0 to 30000 do begin
      Label1.Caption := IntToStr(i);
      Application.ProcessMessages;
    end;
    ReleaseSemaphore(Semaphore, 1, nil);
  end;

  procedure TForm1.FormCreate(Sender: TObject);
  begin
    Semaphore := CreateSemaphore(nil, 1, 1, nil);
  end;

  procedure TForm1.FormDestroy(Sender: TObject);
  begin
    CloseHandle(Semaphore);
  end;

  Jde mi o to, aby kdyz se jiz procedura zpracovava a zavola se znovu,
  aby ke spousteni doslo az po dokonceni prvniho behu procedury.

  V prikladu se to projevi tak, ze kdyz kliknu na tlacitko, spusti se
  vypis cisel. Pokud pri tomto vypisu kliknu jeste jednou, chtel
  bych, aby se druhy vstup do procedury pozastavil a pak az se
  dokonci prvni, zacne se provadet druhy vstup.

  Zatim to funguje ci spis nefunguje tak, ze funkce
  WaitForSingleObject ceka do nekonecna a vypocet procedury se
  pozastavi, takze se nikdy nedokonci a neuvolni semafor.
  V pripade, ze dam misto INFINITE nejakou hodnotu v milisekundach,
  tak program 'stoji' po tuto dobu, vypocet se nedokonci a pak se
  zacne spoustet podruhe.

  Asi na to jdu spatne, nemate nekdo napad, jak to vyresit?

    Petr





--------------------
Nokia 3410 za 4.577 Kč. Nejvýhodnější nabídka telefonu na
trhu!http://user.centrum.cz/redir.php?url=http://www.oskarmobil.cz/handsets/handset.php

Odpovedá: Petr Vones

5. 12. 2002 13:37

From: "Petr Selinger" <delphiforum@centrum.cz>
> procedure TForm1.Button1Click(Sender: TObject);
> var i: integer;
> begin
> WaitForSingleObject(Semaphore, INFINITE);
> for i := 0 to 30000 do begin
> Label1.Caption := IntToStr(i);
> Application.ProcessMessages;
> end;
> ReleaseSemaphore(Semaphore, 1, nil);
> end;
>
> procedure TForm1.FormCreate(Sender: TObject);
> begin
> Semaphore := CreateSemaphore(nil, 1, 1, nil);
> end;
>
> procedure TForm1.FormDestroy(Sender: TObject);
> begin
> CloseHandle(Semaphore);
> end;
>
> Jde mi o to, aby kdyz se jiz procedura zpracovava a zavola se znovu,
> aby ke spousteni doslo az po dokonceni prvniho behu procedury.

Ano, protoze tam mas klasickou chybu v podobe Application.ProcessMessages, kde
se ti zevnitr teto metody zavola cokoli, tedy i znovu metoda ve ktere prave
jsi. Znovu musim opakovat, ze v aplikaci ma byt POUZE JEDINE MISTO ve kterem
se zpracovavaji zpravy (a tedy i z neho volaji ruzne casti kodu jako reakce na
tyto zpravy) a tim uz je Application.Run.

Tvuj problem se da resit dvema zpusoby:

1. Volanim Label1.Refresh pro prekresleni, aplikace bude ovsem jinak stale
   'mrtva'. Toto tedy neni idealni reseni, ale v nekterych pripadech muze byt
   postacujici, hlavne tehdy kdyz GUI aplikace ma zobrazen modalni dialog.

2. Provadeni dane akce dat do samostatneho threadu a hlavni thread aplikace
   ponechat jen pro oblsluhu uzivatelskeho rozhrani. Timto problem vyresis
   uplne, navic se tak (alespon podle mne) lepe vytvari logika cele aplikace,
   protoze umoznuje paralelni beh vice veci. Pomoci VCL action se pak tato
   logika i dobre implementuje.

Petr Vones

Odpovedá: Martin Schayna

5. 12. 2002 14:30

----- Original Message -----
From: "Petr Vones" <pvones@mbox.vol.cz>
> Ano, protoze tam mas klasickou chybu v podobe Application.ProcessMessages,
kde
> se ti zevnitr teto metody zavola cokoli, tedy i znovu metoda ve ktere prave
> jsi. Znovu musim opakovat, ze v aplikaci ma byt POUZE JEDINE MISTO ve kterem
> se zpracovavaji zpravy (a tedy i z neho volaji ruzne casti kodu jako reakce
na
> tyto zpravy) a tim uz je Application.Run.
>
> Tvuj problem se da resit dvema zpusoby:
>
> 1. Volanim Label1.Refresh pro prekresleni, aplikace bude ovsem jinak stale
> 'mrtva'. Toto tedy neni idealni reseni, ale v nekterych pripadech muze byt
> postacujici, hlavne tehdy kdyz GUI aplikace ma zobrazen modalni dialog.

Pozor, na Windows XP to uz nestaci, tam pokud se prepne focus na jinou
aplikaci a zpet, okno "zmrzne" a metoda Refresh nic neprekresli do te doby
nez opet zacne aplikace zpracovavat zpravy.

Abych to (neprilis pekne) obesel, musel jsem si misto
Application.ProcessMessages
volat v te smycce svou obsluhu zprav ala ProcessMessages (porusuji tak pravidlo

POUZE JEDINEHO MISTA), avsak pouze pro vlastni okno a pouze pro
zpravu WM_PAINT:

procedure TForm1.ProcessOwnMessages;
var
  Msg: TMsg;
begin
  while PeekMessage(Msg, Self.Handle, 0, 0, PM_REMOVE) do begin
    // budeme zpracovavat pouze vlastni zpravy a to pouze pro prekreslovani
    if Msg.Message = WM_PAINT then
    begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;
  end;
end;

Samozrejme nejspravnejsi je to opravdu delat v threadu.

Martin Schayna

Odpovedá: Viliam Mlich

6. 12. 2002 2:16

Petr Selinger wrote:

> chtel bych, aby se druhy vstup do procedury pozastavil
> a pak az se dokonci prvni, zacne se provadet druhy vstup.

Na taketo cakanie sa nepouzivaju semafory a kriticke sekcie, ale
'fronta'.

Casto robim aplikacie, kde je na RS 485 zbernici niekolko viac-menej
podobnych mikroprocesorov. Komunikuje sa s nimi tak, ze poslem na
zbernicu poziadavku napriklad 'cislo 4, daj mi obsah registra cislo 12'
a zo zbernice pride odpoved 'cislo 4 ma v registri 12 hodnotu xyz'.
Alebo mu len poviem, ze do registra R ma ulozit hodnotu H a on odpovie
'ok'.

V hlavnom threade vznikaju poziadavky tohoto typu nahodile podla
klikania, casovaca atd.. Pri vzniku kazdej poziadavky sa iba zaradi na
koniec fronty record, obsahujuci cislo procesora, text poziadavky a typ
odpovede.

Po zaradeni poziadavky do fronty sa odblokuje thread, ktory ma frontu
obsluzit, ak este neni odblokovany. (V skutocnosti tych zbernic byva
niekolko a kazda ma vlastnu frontu a vlastny thread na vyberanie z nej,
ale zaradovanie sa robi v hlavnom threade).

Thread obsluhy poziadavky prebieha tak, ze sa najprv data odoslu do
portu a po skonceni vysielania sa urcitu dobu caka na odpoved. Nakoniec
sa odpoved (alebo nic, ak timeout alebo badcrc) podla typu odovzda
vhodnej procedure pre spracovanie vysledku a poziadavka z cela fronty sa
odstrani. While not empty.

Preplneniu fronty sa da zabranit obmedzenim jej dlzky pri vkladani alebo
zahadzovanim 'prestarlych' ci podla ineho hladiska menej nutnych
poloziek, podla toho, ake zotavenie z chyby je pre aplikaciu vhodnejsie.
Ja napriklad stanicu, ktory 3x za sebou neodpovie, preventivne
diskvalifikujem na niekolko desiatok sekund.

Kriticke miesto je sucasne pridavanie a vyberanie z fronty. To musi byt
napisane tak, aby kazda operacia mohla byt v lubovolnom (strojovom!)
kroku prerusena a mohla pokracovat druha.

Ja na to nepouzivam retaz, ale obycajne cyklicke pole s ukazovatkom
konca a zaciatku. 'Pridavac' len skontroluje, ci este ma kam a ak ano,
posunie 'koniec'. A 'vyberac' iba posunie 'zaciatok'. Kazdy si pise do
svojej premennej a tu druhu len cita. A ked sa niektora zmeni ihned po
nacitani, tak 'pridavac' mal proste smolu a porovnava nie celkom
najcerstvejsie data, t.j. mysli si, ze uz vo fronte neni miesto a ono sa
medzitym uvolnilo.

Spracovanie odpovede neprebieha v hlavnom threade, preto si treba dat
bacha na volania VCL, najjednoduchsie je len zaradit nieco hlavnemu
threadu do fronty sprav.

bye
vmlich http://www.rar.cz